package liveSimBE;

import java.util.ArrayList;

public class Circuit {
  ArrayList<ValueChangeListener> valueChangeListeners = new ArrayList<ValueChangeListener> ();
  
  // Parameters
  double dt = 100;

  
  // Circuit state
  double state[] = new double[3];
  String names[] = new String[3];
  {
    names[0] = "Vin";
    names[1] = "Vout";
    names[2] = "Vdd";
  }
  int getIdx(String str) {
    for (int i=0; i<names.length; i++) 
      if (names[i].equals(str))
        return i;
    throw new IndexOutOfBoundsException(str);
  }
  
  void getDerivs(double state[], double delta[]) {
    double Vout = state[getIdx("Vout")];
    double Vin = state[getIdx("Vin")];
    double Vdd = state[getIdx("Vdd")];
//    nmos.setVd(Vout);
//    pmos.setVd(Vout);
    double Ip, In;
    //Ip = pmos.getId();
    //In = nmos.getId();
    double Vt = 1.5;
    if (Vout < Vin-Vt)
      In = 2e-5*((Vin-Vt)*Vout-.5*Math.pow(Vout, 2.0));
    else
      In = .5*2e-5*Math.pow(Vin-Vt,2.0);
    if (Vout >= Vin + Vt)
      Ip = 2e-5*((Vdd-Vin-Vt)*(Vdd-Vout)-.5*Math.pow((Vdd-Vout),2.0));
    else
      Ip = 2e-5/2.0*(Math.pow(Vdd-Vin-Vt,2.0));
    
    delta[getIdx("Vout")] = Ip - In;
    delta[getIdx("Vin")] = 0;
    delta[getIdx("Vdd")] = 0;
  }
  

  public void setVin(double Vin) {
    state[getIdx("Vin")] = Vin;
  }

  public void setVdd(double Vdd) {
    state[getIdx("Vdd")] = Vdd;
  }
  
  double[] addFactor(double[] s, double factor, double[] d) {
    int len = s.length;
    double out[] = new double[len];
    for (int i=0; i<len; i++) 
      out[i] = s[i] + factor*d[i];
    return out;
  }
  
  void add4to(double[] out, double in1[], double in2[], double in3[], double in4[]) {
    for (int i=0; i<out.length; i++)
      out[i] += in1[i] + in2[i] + in3[i] + in4[i];
  }
  
  double[] x2(double in[]) {
    int len = in.length;
    double out[] = new double[len];
    for (int i=0; i<len; i++)
      out[i] = in[i] * 2;
    return out;
  }
  
  public void runUntilStable() {
    double k1[] = new double[3];
    double k2[] = new double[3];
    double k3[] = new double[3];
    double k4[] = new double[3];

    for (int i=0; i<1000; i++) {
      getDerivs(state, k1);
      getDerivs(addFactor(state, .5*dt, k1), k2);
      getDerivs(addFactor(state, .5*dt, k2), k3);
      getDerivs(addFactor(state, dt, k3), k4);
      
      add4to(state, k1, x2(k2), x2(k3), k4);
      
/*      System.out.println("Vout="+Vout+", excessI="+excessI+
          ", nmos("+nmos.getRegion()+" "+In+")"+
          ", pmos("+pmos.getRegion()+" "+Ip+")");*/
      //System.out.printf("Vout=%f k1=%f k2=%f k3=%f k4=%f\n", Vout, k1, k2, k3, k4);
    }
    
  }
  
  public double get_Vout(double Vin, double Vdd) {
    setVin(Vin); setVdd(Vdd);
    runUntilStable();
    return state[getIdx("Vout")];
  }
  
  public static void main(String[] args) {
    Circuit circ = new Circuit();
    double Vin = 2.6, Vdd = 5;
    System.out.println("get_Vout("+Vin+", "+Vdd+")");
    double Vout = circ.get_Vout(Vin, Vdd);
    System.out.println(Vout);
  }
  
  
  
  public static double getMOSFET_Id(double Vg, double Vs, double Vd) {
    MOSFET m = new MOSFET(true);
    m.setVg(Vg); m.setVs(Vs); m.setVd(Vd);
    return m.getId();
  }
  
  public static String getMOSFET_Region(double Vg, double Vs, double Vd) {
    MOSFET m = new MOSFET(true);
    m.setVg(Vg); m.setVs(Vs); m.setVd(Vd);
    return m.getRegion().toString();
  }
}
